【小ネタ】WorkflowsからCloud Functionsの呼び出し時にBearer error="invalid_token"が発生したのでaudienceを明示的に指定するようにして解決した話
リテールアプリ共創部@大阪の岩田です
先日Cloud Functionsを呼び出すWorkflowsを修正している時にBearer error="invalid_token" error_description="The access token could not be verified"
というエラーが発生するようになったので、原因調査と修正を行いました。慣れてる人からすると基本的な内容なのかとは思いますが、原因切り分けに少し時間を要してしまったので、原因と対策についてご紹介します。
正常動作していた頃のWorkflowsとCloud Funtionsの実装
当初WorkflowsからCloud Functionsを呼び出す処理は正常に動作していました。Cloud Functionsの改修を機に正常動作しなくなってしまったのですが、改修前の実装はそれぞれ以下のような実装でした。※実際の実装ではなく説明用に簡略化しています
まずCloud Functionsです
const functions = require('@google-cloud/functions-framework');
functions.http('helloGET', (req, res) => {
res.send('Hello World!');
});
全てのリクエストに対してHello World!
を返却するだけの実装です。このCloud Functionsを呼び出すWorkflowの処理は以下の通りです。
main:
steps:
- call_function:
call: http.get
args:
url: https://asia-northeast1-<プロジェクトID>.cloudfunctions.net/<Cloud Functionsの関数名>
auth:
type: OIDC
http.getを使ってCloud Functionsを呼び出すだけの処理です。Cloud Functionsを呼び出すために認証が必要なので引数のauth
でtype: OIDC
を指定しています。この実装で問題なく動作していました。
動作しなくなったWorkflowsとCloud Funtionsの実装
続いてCloud Functionsのコードに改修が発生しました。改修後のコードは以下の通りです。
const functions = require('@google-cloud/functions-framework');
const express = require('express');
const app = express();
const router = express.Router();
router.get('/hoge', async (req, res) => {
return res.send('hoge');
}
);
router.all('*', async (req, res) => {
res.send('Hello World!');
});
app.use(router);
functions.http('main', app);
従来の処理に加えて、GET /hoge
というリクエストに対してはhoge
を返却する処理を追加しました。そしてWorkflowsからはこの新しい/hoge
というエンドポイントを呼び出すよう改修を加えました。Workflowsの定義は以下の通りです。
main:
steps:
- call_function:
call: http.get
args:
url: https://asia-northeast1-<プロジェクトID>.cloudfunctions.net/<Cloud Functionsの関数名>/hoge
auth:
type: OIDC
http.get
の引数で指定するurl
の末尾に/hoge
を追加しています。この状態でWorkflowsを起動すると...以下のようなエラーが発生しました。
HTTP server responded with error code 401
in step "call_function", routine "main", line: 4
{
"body": "\n<html><head>\n<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n<title>401 Unauthorized</title>\n</head>\n<body text=#000000 bgcolor=#ffffff>\n<h1>Error: Unauthorized</h1>\n<h2>Your client does not have permission to the requested URL <code>/<Cloud Functionsの関数名>/hoge</code>.</h2>\n<h2></h2>\n</body></html>\n",
"code": 401,
"headers": {
"Alt-Svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
"Content-Length": "314",
"Content-Type": "text/html; charset=UTF-8",
"Date": "Sun, 07 Jul 2024 10:28:40 GMT",
"Server": "Google Frontend",
"Www-Authenticate": "Bearer error=\"invalid_token\" error_description=\"The access token could not be verified\""
},
"message": "HTTP server responded with error code 401",
"tags": [
"HttpError"
]
}
URLを少し変更しただけなのになぜ...??
修正後のWorkflows
しばらく色々と悩んだのですが、改めてWorkflowsのドキュメントを確認すると答えが分かりました。
audience
キーは省略できますが、これを使用することで、トークンの OIDC オーディエンスを指定できます。デフォルトでは、OIDC_AUDIENCE
はurl
と同じ値に設定されます。
ワークフローからの認証済みリクエスト | Workflows | Google Cloud
改めてWorkflowsの定義を見直すとaudience
は特に指定していませんでした。ということはurlと同じ値...つまり末尾に/hoge
がついたURLがaudience
として利用されていたということが分かります。ということで明示的にaudience
を指定してあげればエラーは解消しそうです。ということで修正後のWorkflowsの定義です。
main:
steps:
- call_function:
call: http.get
args:
url: https://asia-northeast1-<プロジェクトID>.cloudfunctions.net/<Cloud Functionsの関数名>/hoge
auth:
type: OIDC
audience: https://asia-northeast1-<プロジェクトID>.cloudfunctions.net/<Cloud Functionsの関数名>
これで無事元通りWorkflowsからCloud Functionsを呼び出せるようになりました。めでたしめでたし。
まとめ
WorkflowsからCloud Functionsの呼び出し時に発生した認証エラーについてご紹介しました。やはり脳死でコピペせずにちゃんと公式ドキュメントを読むのは大事ですね。エンジニアとしての基本姿勢ではあるものの、時間が無いときなどは未だにやってしまうことがあります...皆さんもお気を付け下さい。